﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

namespace ColorWheelNET
{
    public partial class ComboBoxNamedColorPicker : UserControl
    {
        Dictionary<string, Color> _defaultColors = null;

        public ComboBoxNamedColorPicker()
        {
            InitializeComponent();
            BuildDefaultColors();
            BindColors();

            cboColors.DrawItem += new DrawItemEventHandler(cboColors_DrawItem);
            cboColors.SelectedValueChanged += new EventHandler(cboColors_SelectedValueChanged);
        }

        void cboColors_SelectedValueChanged(object sender, EventArgs e)
        {
            _text = cboColors.Text;
        }


        string _text = string.Empty;
        public override string Text
        {
            get
            {
                return _text;
            }
            set
            {
                _text = value;
                cboColors.Text = _text;
            }
        }

        public Color Color
        {
            get { return GetColorByName(this.Text); }
            set
            {
                this.Text = GetNameFromColor(value);
            }
        }

        private string GetNameFromColor(System.Drawing.Color value)
        {
            if (BindingMode == ColorBindingMode.CustomList && CustomNamedColors != null && CustomNamedColors.Values.Contains(value))
            {
                var name = 
                    from entry in CustomNamedColors 
                    where entry.Value == value 
                    select entry.Key;

                return name.ToString();
            }

            return value.Name;
        }

        private Color GetColorByName(string name)
        {
            if (string.IsNullOrEmpty(name)) { return Color.Empty; }

            Color c = Color.Empty;

            if (this.BindingMode == ColorBindingMode.CustomList && CustomNamedColors != null)
            {
                if (CustomNamedColors.TryGetValue(name, out c))
                {
                    return c;
                }
            }

            if (this.BindingMode == ColorBindingMode.SystemDefined)
            {
                if (_defaultColors.TryGetValue(name, out c))
                {
                    return c;
                }
            }
            
            //not found
            return c;
        }


        void cboColors_DrawItem(object sender, DrawItemEventArgs e)
        {
            try
            {
                string name = e.Index >= 0 ? cboColors.Items[e.Index].ToString() : cboColors.Text;

                if (string.IsNullOrEmpty(name)) { return; }

                e.DrawBackground();

                Rectangle rectangle = new Rectangle(2, e.Bounds.Top + 2, 17, e.Bounds.Height - 4); //width of 17 seems good...
                e.Graphics.FillRectangle(new SolidBrush(GetColorByName(name)), rectangle);
                e.Graphics.DrawString(name, cboColors.Font, System.Drawing.Brushes.Black, new RectangleF(e.Bounds.X + rectangle.Width, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height));

                e.DrawFocusRectangle();
            }
            finally { }
        }

        private void BuildDefaultColors()
        {
            _defaultColors = new Dictionary<string, Color>();
            foreach (PropertyInfo p in typeof(Color).GetProperties())
            {
                if (p.PropertyType == typeof(Color))
                {
                    Color c = Color.Empty;
                    c = (Color)p.GetValue(c, null);
                    if (c.IsEmpty) { continue; }
                    _defaultColors[c.Name] = c;
                }
            }
        }


        bool _allowEmpty = true;
        public bool AllowEmptyColor
        {
            get { return _allowEmpty; }
            set { _allowEmpty = value; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)]
        /// <summary>
        /// Names of Colors To Exclude
        /// </summary>
        public List<Color> ExcludedColors { get; protected set; }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)]
        /// <summary>
        /// Custom Colors to put in the drop down.
        /// 
        /// Key = String: Name of the color
        /// Value = Color : the actual color (rgb)
        /// 
        /// Mode must be set to allow custom colors
        /// </summary>
        public Dictionary<string, Color> CustomNamedColors { get; set; }

        public ColorBindingMode BindingMode { get; set; }

        public void Bind()
        {
            BindColors();
        }

        public void Bind(List<Color> excludedColors)
        {
            ExcludedColors = excludedColors ?? ExcludedColors;
            BindColors();
        }

        public void Bind(Dictionary<string, Color> customNamedColors, ColorBindingMode bindingMode = (ColorBindingMode.CustomList & ColorBindingMode.SystemDefined), List<Color> excludedColors = null)
        {
            BindingMode = bindingMode;
            CustomNamedColors = customNamedColors;
            ExcludedColors = excludedColors;
            BindColors();
        }

        private void BindColors()
        {
            List<string> namedColors = new List<string>();

            if (BindingMode == ColorBindingMode.SystemDefined)
            {
                namedColors.AddRange(_defaultColors.Keys.ToArray());
            }

            if (BindingMode == ColorBindingMode.CustomList && CustomNamedColors != null)
            {
                namedColors.AddRange(CustomNamedColors.Keys.ToArray());
            }

            if (ExcludedColors != null)
            {
                foreach (Color s in ExcludedColors)
                {
                    namedColors.Remove(s.Name);
                }
            }

            namedColors.Sort();

            if (AllowEmptyColor)
            {
                cboColors.Items.Add(string.Empty);
            }

            cboColors.Items.AddRange(namedColors.Distinct().ToArray());

            this.Text = this.Text;
        }

        [Flags]
        public enum ColorBindingMode : short
        {
            SystemDefined = 0,
            CustomList = 1
        }

    }
}
